/*
	ligregni@freebsd.org
*/

#include <libgeom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/disk.h>

int i;
#define PT \
	for (i=0; i<level; ++i)	printf("  ");

#define PN printf("\n");

void print_gmesh(struct gmesh *gmh, int level, int);
void print_gclass(struct gclass *gcl, int level, int);
void print_gprovider(struct gprovider *gpr, int level, int);
void print_gconsumer(struct gconsumer *gcn, int level, int);
void print_ggeom(struct ggeom *ggm, int level, int);
void print_gconf(struct gconf *gcn, int level, int);


static int get_config_for_part(struct gmesh *mesh, const char *geom_class, const char *geom, const char *prv, struct gconf *gcn);
static int get_config_for_geom(struct gmesh *mesh, const char *geom_class, const char *geom, struct gconf *gcn);
static int get_partitions(struct gmesh *mesh, const char *geom, char **result, int *part_count);
static int init_geom (struct gmesh *mesh);

static int
init_geom (struct gmesh *mesh)
{
	return geom_gettree(mesh);
}

const char *
get_geom_config( const char *geom_class,
							const char *geom, const char *prv, const char *parameter)
{
	struct gmesh mesh;
	struct gconf gcn;
	struct gconfig *gcfg;
	char *result = NULL;

	if (init_geom(&mesh))
		return NULL;

	if (prv) /* We got a provider, so dig one level more in the tree */
	{
		if (get_config_for_part(&mesh, geom_class, geom, prv, &gcn))
			return NULL;
	}
	else /* just get the GEOM's config (normally disks for the PART class) */
		if (get_config_for_geom(&mesh, geom_class, geom, &gcn))
			return NULL;


	LIST_FOREACH(gcfg, &gcn, lg_config)
	{
		if ( !strcmp(gcfg->lg_name, parameter) )
		{
			result = (char *) malloc(strlen(gcfg->lg_val)+1);
			strncpy(result, gcfg->lg_val, sizeof(result));
			break;
		}
	}

	geom_deletetree(&mesh);

	return result;
}

static int
get_config_for_part(struct gmesh *gmh, const char *geom_class, const char *geom, const char *prov, struct gconf *gcn)
{
	struct gclass *gcls;
	LIST_FOREACH(gcls, &(gmh->lg_class), lg_class)
	{
		if (strncmp(gcls->lg_name, geom_class, strlen(gcls->lg_name)))
			continue;
		struct ggeom *ggom;
		LIST_FOREACH(ggom, &(gcls->lg_geom), lg_geom)
		{
			if (strncmp(ggom->lg_name, geom, sizeof(ggom->lg_name)))
				continue;
			struct gprovider *gprv;
			LIST_FOREACH(gprv, &(ggom->lg_provider), lg_provider)
			{
					if (!strncmp(gprv->lg_name, prov, sizeof(prov)))
					{
						memcpy(gcn, &(gprv->lg_config), sizeof(struct gconf));
						return 0;
					}
			}
		}
	}
	return -1;
}

static int
get_config_for_geom(struct gmesh *gmh, const char *geom_class, const char *geom, struct gconf *gcn)
{
	struct gclass *gcls;
	LIST_FOREACH(gcls, &(gmh->lg_class), lg_class)
	{
		if (strncmp(gcls->lg_name, geom_class, strlen(gcls->lg_name)))
			continue;
		struct ggeom *ggom;
		LIST_FOREACH(ggom, &(gcls->lg_geom), lg_geom)
		{
			if (!strncmp(ggom->lg_name, geom, sizeof(ggom->lg_name)))
			{
				memcpy(gcn, &(ggom->lg_config), sizeof(struct gconf));
				return 0;
			}
		}
	}
	return -1;
}


static int
get_partitions(struct gmesh *gmh, const char *geom, char **result, int *part_count)
{
	int count = 0;

	if ( !result )
		*part_count = 0;

	struct gclass *gcls;
	LIST_FOREACH(gcls, &(gmh->lg_class), lg_class)
	{
		if (strncmp(gcls->lg_name, "PART", strlen(gcls->lg_name)))
			continue;
		struct ggeom *ggom;
		LIST_FOREACH(ggom, &(gcls->lg_geom), lg_geom)
		{
			if (strncmp(ggom->lg_name, geom, sizeof(ggom->lg_name)))
				continue;
			struct gprovider *gprv;
			LIST_FOREACH(gprv, &(ggom->lg_provider), lg_provider)
			{
				if ( result && count < (*part_count) )
				{
					*(result+count) = (char *) malloc(sizeof(gprv->lg_name));
					if ( !(*(result+count)) )
						return -1;
					strncpy( *(result+count), gprv->lg_name, sizeof(*(result+count)) );
				}
				++count;
			}
		}
	}
	*part_count = count;
	return 0;
}

int
get_partition_array(const char *geom, const char ***array)
{
	struct gmesh mesh;
	char **result = NULL;
	int part_count;

	if (init_geom(&mesh))
		return 0;

	if (get_partitions(&mesh, geom, NULL, &part_count) < 0)
		return 0;

	result = (char **) malloc(sizeof(char *) * part_count);

	if (!result)
		return 0;

	if (get_partitions(&mesh, geom, result, &part_count) < 0)
		return 0;

	if (result)
		*array = (const char **) result;

	return part_count;
}

void print_gclass(struct gclass *gcl, int level, int who)
{
/*
struct gclass {
	void			*lg_id;
	char			*lg_name;
	LIST_ENTRY(gclass)	lg_class;
	LIST_HEAD(, ggeom)	lg_geom;
	struct gconf		lg_config;
};
*/
	struct ggeom *ggom;
	if (!gcl || who == 5)
		return;
	PT		printf("a:%s", gcl->lg_name);			PN
	LIST_FOREACH(ggom, &(gcl->lg_geom), lg_geom)
		print_ggeom(ggom, level+1, 2);
	print_gconf(&(gcl->lg_config), level+1, 2);
	PN
}

void print_gprovider(struct gprovider *gpr, int level, int who)
{
/*
struct gprovider {
	void			*lg_id;
	char			*lg_name;
	struct ggeom		*lg_geom;
	LIST_ENTRY(gprovider)	lg_provider;
	LIST_HEAD(, gconsumer)	lg_consumers;
	char			*lg_mode;
	off_t			lg_mediasize;
	u_int			lg_sectorsize;
	off_t			lg_stripeoffset;
	off_t			lg_stripesize;
	struct gconf		lg_config;
};
*/
	struct gconsumer *gcns;
	if (!gpr || who == 4)
		return;
	PT		printf("b:%s", gpr->lg_name);			PN
	print_ggeom(gpr->lg_geom, level+1, 3);
	LIST_FOREACH(gcns, &(gpr->lg_consumers), lg_consumer)
		print_gconsumer(gcns, level+1, 3);
	PT		printf("CONSUMERS:");				PN
	LIST_FOREACH(gcns, &(gpr->lg_consumers), lg_consumers)
		print_gconsumer(gcns, level+1, 3);
	PT		printf("%s", gpr->lg_mode);			PN
	PT		printf("%ld", gpr->lg_mediasize);	PN
	PT		printf("%u", gpr->lg_sectorsize);	PN
	PT		printf("%ld", gpr->lg_stripeoffset);PN
	PT		printf("%ld", gpr->lg_stripesize);	PN
	print_gconf(&(gpr->lg_config), level+1, 3);
	PN
}

void print_gconsumer(struct gconsumer *gcn, int level, int who)
{
/*
struct gconsumer {
	void			*lg_id;
	struct ggeom		*lg_geom;
	LIST_ENTRY(gconsumer)	lg_consumer;
	struct gprovider	*lg_provider;
	LIST_ENTRY(gconsumer)	lg_consumers;
	char			*lg_mode;
	struct gconf		lg_config;
};
*/
	if (!gcn || who == 5 || who == 3)
		return;
	print_ggeom(gcn->lg_geom, level+1, 4);
	print_gprovider(gcn->lg_provider, level+1, 4);
	PT		printf("c:%s", gcn->lg_mode);			PN
	print_gconf(&(gcn->lg_config), level+1, 4);
	PN
}

void print_ggeom(struct ggeom *ggm, int level, int who)
{
/*
struct ggeom {
	void			*lg_id;
	struct gclass		*lg_class;
	char			*lg_name;
	u_int			lg_rank;
	LIST_ENTRY(ggeom)	lg_geom;
	LIST_HEAD(, gconsumer)	lg_consumer;
	LIST_HEAD(, gprovider)	lg_provider;
	struct gconf		lg_config;
};
*/
	struct gconsumer *gcns;
	struct gprovider *gprv;

	if (!ggm || who == 3)
		return;

	print_gclass(ggm->lg_class, level+1, 5);
	PT		printf("d:%s", ggm->lg_name);			PN
	PT		printf("%u", ggm->lg_rank);			PN
	LIST_FOREACH(gcns, &(ggm->lg_consumer), lg_consumer)
		print_gconsumer(gcns, level+1, 5);
	PT		printf("CONSUMERS:");				PN
	LIST_FOREACH(gcns, &(ggm->lg_consumer), lg_consumers)
		print_gconsumer(gcns, level+1, 5);
	LIST_FOREACH(gprv, &(ggm->lg_provider), lg_provider)
		print_gprovider(gprv, level+1, 5);
	print_gconf(&(ggm->lg_config), level+1, 5);
	PN
}

void print_gconf(struct gconf *gcn, int level, int who)
{
/*
struct gconfig {
	LIST_ENTRY(gconfig)	lg_config;
	char			*lg_name;
	char			*lg_val;
};
*/
	struct gconfig *gcfg;

	if (!gcn)
		return;

	//gcn->lh_first;

	LIST_FOREACH(gcfg, gcn,	lg_config)
	{
		PT	printf("PARAMETRO: %s", gcfg->lg_name);
		printf(": %s", gcfg->lg_val);			PN
	}
	PN
}
